Xcodeからバックエンドを作る!Amplify iOSを試してみた #reinvent
Amplify iOS
Amplify iOSおよびAmplify Androidは、AWS re:Invent 2019で発表されたAmplify Frameworkの新しいツール・ライブラリです。現在はプレビュー版として提供されています。
本記事ではGetting Startedに沿ってiOSアプリを作ってみました。
サンプルコードをGitHubで公開しましたので、合わせてご確認いただけますと幸いです。
インストール
Xcodeプロジェクト、およびCocoaPodsはすでに作成・導入済みの前提で進みます。本記事では AmplifySample
という名前のXcodeプロジェクトを作成しました。
まずはXcodeプロジェクトのディレクトリを開き Podfile
を作成します。
$ cd ./AmplifySample $ pod init
Podfile
は以下のようにします。
target 'AmplifySample' do use_frameworks! # Pods for AmplifySample pod 'amplify-tools' pod 'Amplify' pod 'AWSPluginsCore' pod 'AmplifyPlugins/AWSAPIPlugin' end
pod install
を実行し、各ライブラリをインストールします。
$ pod install --repo-update
Xcodeワークスペースが作成されます AmplifySample.xcworkspace
を開きビルドを行います。ビルドは Command + B で行えます。
ビルドすると下図のように3つの設定ファイルが生成されます。
それぞれ次のような設定が含まれるようになります。
awsconfiguration.json
とamplifyconfiguration.json
: AmplifyとしてAWSの各リソースを作る際の設定ファイルamplifyxc.config
: XcodeからAmplify Frameworkの操作を行う際の設定ファイル
モデルの作成
Amplify iOSでは、デフォルトでGraphQL (AppSync)と通信することでデータを取得したりする前提で各設定が行われています。
amplify/backend/api/amplifyDatasource/schema.graphql
にファイルが作成されており、確認してみると下記のようなGraphQLスキーマが作られています。
type Task @model { id: ID! title: String! description: String status: String } type Note @model { id: ID! content: String! }
このGraphQL Schemaを元に、モデルクラスを作ってみます。モデルクラスを作る場合は amplifyxc.config
の設定を変更し modelgen=true
にします。
push=false modelgen=true profile=default envName=amplify
ビルドすると次のファイルが amplify/generated/models
に生成されます。
AmplifyModels.swift
Note.swift
Note+Schema.swift
Task.swift
Task+Schema.swift
生成したファイルはXcode ProjectのTarget内に追加します。
最後にビルドしておきます。
APIとDatabaseの追加
push=true modelgen=true profile=default envName=amplify
profile
は ~/.aws/config
に設定してあるProfileを利用することが可能です。しかしMFAには対応していないようなのでご留意ください。MFAが不要なIAM Userで実行可能です。
ビルドを実行するとAWSアカウント上に各種リソースが作成されます。awsconfiguration.json
と amplifyconfiguration.json
に設定が増えていることが確認できます。
{ "UserAgent": "aws-amplify/cli", "Version": "0.1.0", "IdentityManager": { "Default": {} }, "AppSync": { "Default": { "ApiUrl": "https://<api id>.appsync-api.ap-northeast-1.amazonaws.com/graphql", "Region": "ap-northeast-1", "AuthMode": "API_KEY", "ApiKey": "<api key>", "ClientDatabasePrefix": "amplifyDatasource_API_KEY" } } }
{ "api": { "plugins": { "awsAPIPlugin": { "amplifyDatasource": { "endpointType": "GraphQL", "endpoint": "https://<api id>.appsync-api.ap-northeast-1.amazonaws.com/graphql", "region": "ap-northeast-1", "authorizationType": "API_KEY", "apiKey": "<api key>" } } } } }
APIを利用したアプリの実装
ここまででAPIの用意ができたので、アプリにAPIを呼び出す処理を実装します。
AppDelegate での初期化
AppDelegate.swift
の application:didFinishLaunchingWithOptions:
メソッド内に次の処理を追加します。
import UIKit import Amplify import AmplifyPlugins @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let apiPlugin = AWSAPIPlugin(modelRegistration: AmplifyModels()) do { try Amplify.add(plugin: apiPlugin) try Amplify.configure() print("Amplify initialized") } catch { print("Failed to configure Amplify \(error)") } return true } }
View Controllerで書き込み/読み込みを試す
次にView Controller内でAPIに対してのデータの書き込み/読み込みを試していきたいと思います。
まずView Controllerでデータの確認ができるよう、下図のようにViewを配置します。
続いてView Controllerの実装です。以下のように実装しました。
import UIKit import Amplify class ViewController: UIViewController { @IBOutlet weak var textField: UITextField! @IBOutlet weak var tableView: UITableView! var list = [Note]() override func viewDidLoad() { super.viewDidLoad() query() subscribe() } @IBAction func postButtonDidTouch(_ sender: Any) { let content = textField.text ?? "" let note = Note(content: content) _ = Amplify.API.mutate(of: note, type: .create) { (event) in switch event { case .completed(let result): switch result { case .success(let note): print("API Mutate successful, created note: \(note)") DispatchQueue.main.async { self.textField.text = nil } case .failure(let error): print("Completed with error: \(error.errorDescription)") } case .failed(let error): print("Failed with error \(error.errorDescription)") default: print("Unexpected event") } } } func query() { _ = Amplify.API.query(from: Note.self, where: nil) { (event) in switch event { case .completed(let result): switch result { case .success(let notes): print("Successfully retrieved list of notes: \(notes)") self.list = notes DispatchQueue.main.async { self.tableView.reloadData() } case .failure(let error): print("Got failed result with \(error.errorDescription)") } case .failed(let error): print("Got failed event with error \(error)") default: print("Should never happen") } } } func subscribe() { _ = Amplify.API.subscribe(from: Note.self, type: .onCreate) { (event) in switch event { case .inProcess(let subscriptionEvent): switch subscriptionEvent { case .connection(let subscriptionConnectionState): print("Subsription connect state is \(subscriptionConnectionState)") case .data(let result): switch result { case .success(let note): print("Successfully got todo from subscription: \(note)") self.list.append(note) DispatchQueue.main.async { self.tableView.reloadData() } case .failure(let error): print("Got failed result with \(error.errorDescription)") } } case .completed: print("Subscription has been closed") case .failed(let error): print("Got failed result with \(error.errorDescription)") default: print("Should never happen") } } } } extension ViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return list.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .default, reuseIdentifier: "Cell") let note = list[indexPath.row] cell.textLabel?.text = note.content return cell } }
上記の実装コードのポイントは下記の通りです。
- View Controllerが表示された際にSubscribeを開始
- TextInputに入力し、Buttonで投稿をしたらAPIにリクエスト
- 新しいデータが作成されたらSubscribeイベントが到着
- 新しいデータをTableViewに反映
以上で完了です。
動かす
それではiOSアプリとして動かしてみます。Command + R でデバッグ起動します。何かしらを書き込んでみます。投稿するとクラウド上にデータが保存され、TableViewにアイテムが追加されます。
バックエンド側を簡単に確認してみます。CloudFormationを見てもらうと分かりますが、Amplify経由でAppSyncやDynamoDB、Cognito User Poolといったリソースが作成されています。
DynamoDBのNoteテーブルを確認してみると、先ほど追加したデータが格納されていることが分かります。
コマンドラインでの操作不要でAmplify Frameworkが使える!
Amplify FrameworkではAmplify CLIをコマンドラインで実行しなければAPIの作成などは行えませんでした。今回のAmplify iOSおよびAmplify AndroidではXcodeなどの統合開発環境(IDE)からAmplify Frameworkの操作が行えます。これはモバイルアプリ開発者にとっては非常に嬉しい限りですね!
他にもPredictionsなどの面白い機能があるので、試していきたいと思います。